// Copyright (c) 2023 Presto Labs Pte. Ltd.
// Author: tianqiang

#include "coin2/exchange/mexc/api_util_v3/rate_tracker.h"

namespace coin2::exchange::mexc::api_util_v3 {

constexpr auto EXAMPLE_SPOT_MEXC = R"(
"connection_config": {
  "key_filepath": "/home/ec2-user/config/trade/xunke18/mexc/trade_key.json",
  "rate_limit_info": {
    "spot_mexc": {
      "weight_per_ip": {
        "secs": 60,
        "reqs": 20000
      },
      "weight_per_uid": {
        "secs": 60,
        "reqs": 240000
      }
    }
  }
},)";

template <class RateTracker>
void CheckFail(RateTracker&& rate_tracker, bool check_result) {
  if (!check_result) {
    LOG(INFO) << "Hit rate limit on [" << rate_tracker->GetName() << "]";
  }
}

MexcRateTracker::MexcRateTracker(const MarketExchangeApi& mea, const OrderConnectionConfig& cfg) {
  if (mea.market == coin::proto::MarketType::Spot) {
    RateLimitInfoSpotMexc rate_config;
    RateConfig::Get()->GetConfig(mea, cfg, &rate_config);
    InitRateTracker(&rate_config, mea.String(), EXAMPLE_SPOT_MEXC);
  } else {
    CHECK(false) << "unsupported mea: " << mea.String();
  }
}

void MexcRateTracker::InitRateTracker(
    RateLimitInfoSpotMexc* rate_config,
    std::string_view mea_str,
    std::string_view example) {
  if (!rate_config || !rate_config->has_weight_per_ip() || !rate_config->has_weight_per_uid()) {
    RateConfig::CheckFail(mea_str, example);
  }
  const auto& weight_per_ip = rate_config->weight_per_ip();
  const auto& weight_per_uid = rate_config->weight_per_uid();
  LOG(INFO) << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
            << "\nOG request rate was set as below:\n"
            << "\nweight_per_ip: [" << weight_per_ip.reqs() << "(weights)/" << weight_per_ip.secs()
            << "(secs)]"
            << "\nweight_per_uid: [" << weight_per_uid.reqs() << "(weights)/"
            << weight_per_uid.secs() << "(secs)]"
            << "\n\nOr you can configure customized rate in driver config. For example: " << example
            << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~";

  rate_tracker_ip_ =
      std::make_unique<FixedWindowRateTracker>(weight_per_ip.secs() * 1'000'000'000LL);
  rate_tracker_ip_->SetRateLimitInfo(weight_per_ip.reqs(), weight_per_ip.reqs());
  rate_tracker_ip_->SetName("mexc_rate_tracker_ip(60 secs)");

  rate_tracker_uid_ =
      std::make_unique<FixedWindowRateTracker>(weight_per_uid.secs() * 1'000'000'000LL);
  rate_tracker_uid_->SetRateLimitInfo(weight_per_uid.reqs(), weight_per_uid.reqs());
  rate_tracker_uid_->SetName("mexc_rate_tracker_uid(60 secs)");

  rate_limit_report_writer_ = std::make_unique<RateLimitReportWriter>();
  rate_limit_report_writer_->SetRateLimitInfoString(rate_config);
}

bool MexcRateTracker::HasEnoughRateLimit(int64_t timestamp, ActionType action_type) {
  if (CheckEnableTime(timestamp, action_type) && CheckRateLimit(timestamp, action_type)) {
    return true;
  }

  rate_limit_report_writer_->SetHitRateLimit(timestamp, action_type);
  return false;
}

void MexcRateTracker::Push(int64_t timestamp, int64_t ip_weight, int64_t uid_weight) {
  if (ip_weight > 0) {
    rate_tracker_ip_->Push(ip_weight, timestamp);
  }

  if (uid_weight > 0) {
    rate_tracker_uid_->Push(uid_weight, timestamp);
  }
}

void MexcRateTracker::SetRateLimitInfoString(const google::protobuf::Message* message) {
  if (!rate_limit_report_writer_) {
    return;
  }
  rate_limit_report_writer_->SetRateLimitInfoString(message);
}

void MexcRateTracker::WriteRateLimitReportMessage(OrderLoggerCB callback) {
  if (!callback) {
    return;
  }
  rate_limit_report_writer_->Write(callback);
}

double MexcRateTracker::GetMaxRateUsedRatio(int64_t timestamp, ActionType action_type) {
  return std::max(
      GetMaxRateUsedRatio(rate_tracker_ip_, timestamp),
      GetMaxRateUsedRatio(rate_tracker_uid_, timestamp));
}

bool MexcRateTracker::CheckEnableTime(int64_t timestamp, ActionType action_type) {
  if (timestamp >= enable_time_) {
    return true;
  }

  LOG_EVERY_N(WARNING, 50) << "Hit api temporary disabled until " << enable_time_ << ", "
                           << action_type;
  return false;
}
bool MexcRateTracker::CheckRateLimit(int64_t timestamp, ActionType action_type) {
  bool ret = true;

  ret = rate_tracker_ip_->GetRemaining(timestamp) > 1;
  CheckFail(rate_tracker_ip_, ret);

  if (ret) {
    ret = rate_tracker_uid_->GetRemaining(timestamp) > 1;
    CheckFail(rate_tracker_uid_, ret);
  }

  return ret;
}
}  // namespace coin2::exchange::mexc::api_util_v3
